﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.ValueProviders;
using Newtonsoft.Json;

using ProjectBase.Core;

using $prjsn$.Common;

namespace $safeprojectname$
{
    [EntityConfiguration]
    [ActionSelectFilter]
    [ExceptionHandler]
    [ErrorInputHandler]
    //[ValidateAntiForgeryToken]
    [LogFilter]
    public abstract class EntityService<TEntity, TFilter, TFacade>
        : ApiService
          , IFilterApiBinder<TFilter, int>
          , IEntityService
        where TEntity : class, IPersistentObjectWithTypedId<int>
        where TFilter : class, IFilter<int>
        where TFacade : class, IEntityFacade<TEntity, int, TFilter>
    {
        #region Init

        protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext context)
        {
            base.Initialize(context);

            var jSettings = context.Configuration.Formatters.JsonFormatter.SerializerSettings;

            // The POST and PUT method resolver
            EntityResolver.EntityConverter.Context = context;

            if (context.Request.Method.Equals(HttpMethod.Get))
            {
                jSettings.NullValueHandling = (NullValueHandling)Project.Settings.NullValueHandling;
            }

            jSettings.ContractResolver = EntityResolver;
        }

        #endregion Init

        #region Get by ID

        [HttpGet]
        public virtual HttpResponseMessage GetById(int id)
        {
            var entity = Facade.GetById(id);

            var messages = new ServiceMessage();
            var response = Request.CreateResponse(HttpStatusCode.OK, entity);

            if (entity.IsNull())
            {
                messages.Add(Severity.Error, "Entity with the ID [{0}] was not found".FormatWith(id));
                response.StatusCode = HttpStatusCode.NotFound;
            }

            return response;
        }

        #endregion Get by ID

        #region Find

        [HttpGet]
        [ListFilter]
        public virtual IEnumerable<TEntity> Find(TFilter filter)
        {
            return Facade.Find(filter);
        }

        #endregion Find

        #region Quey OData

        [HttpGet]
        [ListFilter]
        public virtual IEnumerable<TEntity> Query(TFilter filter)
        {
            return Facade.Find(filter);
        }

        #endregion Quey OData

        #region New

        [HttpGet]
        public virtual TEntity New()
        {
            var entity = Facade.CreateNew();
            return entity;
        }

        #endregion New

        #region Add

        [HttpPost]
        [Transaction]
        public virtual HttpResponseMessage Add(TEntity entity)
        {
            var messages = new ServiceMessage();

            // 400
            entity = Facade.Add(entity);
            if (Facade.Messages.ContainsError())
            {
                messages.Add(Severity.Warning, "Entity cannot be created");
                foreach (var message in Facade.Messages)
                {
                    messages.Add(message.Severity, message.Text);
                }
                return Request.CreateResponse(HttpStatusCode.BadRequest, messages);
            }

            // ok 201 Created
            var response = Request.CreateResponse(HttpStatusCode.Created);

            string uri = Url.Link("DefaultApi", new { id = entity.ID });
            response.Headers.Location = new Uri(uri);

            return response;
        }

        #endregion Add

        #region Update

        [HttpPut]
        [Transaction]
        public virtual HttpResponseMessage Update(int id, TEntity entity)
        {
            var messages = new ServiceMessage();

            // 404
            if (entity.IsNull() || !entity.ID.Equals(id))
            {
                messages.Add(Severity.Error, "Entity with the ID [{0}] was not found".FormatWith(id));
                return Request.CreateResponse(HttpStatusCode.NotFound, messages);
            }

            // 400
            entity = Facade.Update(entity);
            if (Facade.Messages.ContainsError())
            {
                messages.Add(Severity.Warning, "Entity with the ID [{0}] cannot be updated".FormatWith(id));
                foreach (var message in Facade.Messages)
                {
                    messages.Add(message.Severity, message.Text);
                }
                return Request.CreateResponse(HttpStatusCode.BadRequest, messages);
            }

            // ok 204 NoContent
            var response = Request.CreateResponse(HttpStatusCode.NoContent);

            string uri = Url.Link("DefaultApi", new { id = entity.ID });
            response.Headers.Location = new Uri(uri);

            return response;
        }

        #endregion Update

        #region Delete

        [HttpDelete]
        [Transaction]
        public virtual HttpResponseMessage Delete(int id)
        {
            var entity = Facade.GetById(id);
            var messages = new ServiceMessage();

            // 404
            if (entity.IsNull())
            {
                messages.Add(Severity.Error, "Entity with the ID [{0}] was not found".FormatWith(id));
                return Request.CreateResponse(HttpStatusCode.NotFound, messages);
            }

            // 400
            Facade.Delete(entity);
            if (Facade.Messages.ContainsError())
            {
                messages.Add(Severity.Warning, "Entity with the ID [{0}] cannot be deleted".FormatWith(id));
                foreach (var message in Facade.Messages)
                {
                    messages.Add(message.Severity, message.Text);
                }
                return Request.CreateResponse(HttpStatusCode.BadRequest, messages);
            }

            // ok 204 NoContent
            return Request.CreateResponse(HttpStatusCode.NoContent);
        }

        #endregion Delete

        #region special Action

        [HttpPut]
        public virtual HttpResponseMessage SpecialAction(string actionName, int? id = null)
        {
            //TODO action
            var response = Request.CreateResponse<TEntity>(HttpStatusCode.NoContent, null);

            string uri = Url.Link("DefaultApi", new { id = id });
            response.Headers.Location = new Uri(uri);
            return response;
        }

        #endregion special Action

        #region MetaData

        [HttpGet]
        public virtual MetaDataMessage MetaData(int id)
        {
            var metaData = MetaDataProvider.GetMetaData(id);
            return metaData;
        }

        [HttpGet]
        public virtual MetaDataMessage MetaData()
        {
            var metaData = MetaDataProvider.GetMetaData();
            return metaData;
        }

        #endregion MetaData

        #region IFilterApiBinder

        // explicit
        TFilter IFilterApiBinder<TFilter, int>.BindFilter(IValueProvider valueProvider)
        {
            return BindFilter(valueProvider);
        }

        // protected to be overriden if needed
        protected virtual TFilter BindFilter(IValueProvider valueProvider)
        {
            var filter = Facade.CreateNewFilter();

            Facade.BindFilter(filter, valueProvider.ToContainer());

            return filter;
        }

        #endregion IFilterApiBinder

        #region properties

        public virtual Messages Messages { get; set; }

        public virtual TFacade Facade { protected get; set; }

        public virtual EntityResolver<TEntity, int> EntityResolver { protected get; set; }

        public virtual IRequestResolver RequestResolver { get { return EntityResolver; } }

        public virtual IMetaDataProvider<TEntity, int> MetaDataProvider { protected get; set; }

        IMetaDataProvider IEntityService.MetaDataProvider { get { return MetaDataProvider; } }

        #endregion properties
    }
}